#pragma once
#include<iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
 #include <errno.h>
 #include <strings.h>
 #include <stdlib.h>
  #include <string.h>
  #include<unistd.h>
  #include<functional>
using namespace std;

namespace udpServer
{
   static const  string defaultIP="0.0.0.0";//默认IP地址
   static int SIZE=1024;
enum {USAGE_ERR=1,SOCKET_ERR,BIND_ERR,OPEN_ERR};//用枚举函数定义各自错误的返回值
typedef function<void(int,string,uint16_t,string)> func_t;//仿函数--调用的函数类型
    class udpserver
    {
public:

udpserver(const func_t& callback ,const uint16_t& port,const string& ip=defaultIP)
:_callback(callback)
,_port(port)
,_ip(ip)
,_sockfd(-1)
{}

void initudpserver()
{
    //1.创建套接字
_sockfd=socket(AF_INET,SOCK_DGRAM,0);//创建套接字文件描述符
if(_sockfd==-1)
{
cerr<<"socket err"<<errno<<" : "<<strerror(errno)<<endl;
exit(SOCKET_ERR);
}
 cout<<"socket success"<<": "<< _sockfd<<endl;
//2.绑定port端口号
//2.1将port和ip填入结构体中，该结构体可以理解成用户定义的数据或用户栈
struct sockaddr_in local;//创建结构
bzero(&local,sizeof(local));//将结构清零
local.sin_family=AF_INET;//填充协议家族
local.sin_port=htons(_port);//填充端口号。htons将port主机序列转化为网络序列
//local.sin_addr.s_addr=inet_addr(_ip.c_str());
//填充ip地址。inet_addr函数作用：将ip地址的string类型转化为uint32_t，其次ip地址的将主机序列转化为网络序列
local.sin_addr.s_addr=htons(INADDR_ANY);//不绑定指定ip，可以接收任何传达到指定端口号的ip主机发的数据
//2.2将sockaddr_in与套接字进行绑定
int n=bind(_sockfd,(struct sockaddr*)&local,sizeof(local));
if(n==-1)
{
cerr<<"bind err"<<errno<<" : "<<strerror(errno)<<endl;
exit(BIND_ERR);
}
}

void start()
{
    while(true)
    {
        char buffer[SIZE];//缓冲区
       struct sockaddr_in out;
       socklen_t len=sizeof(out);
        ssize_t s=recvfrom(_sockfd,buffer,sizeof(buffer)-1,0,(sockaddr*)&out,&len);
        if(s>=0)
        {
            buffer[s]=0;
            string clientip=inet_ntoa(out.sin_addr);//网络序列转换为主机序列；uin32t_t->string
            uint16_t clientport=ntohs(out.sin_port);//网络序列转化为主机序列
            string message=buffer;
            cout<<clientip<<"["<<clientport<<"]# "<<message<<endl;
            _callback(_sockfd,clientip,clientport,message);//调用外部函数处理数据
        }
}
}

~udpserver()
{}

private:
uint16_t _port;//端口号
string _ip;//ip地址
int _sockfd;//套接字文件描述符
func_t _callback;
    };
}
